package metaballs;

import java.nio.FloatBuffer;

import util.Vec2F;


public class Metaball
{
	private Vec2F location;
	private Vec2F velocity;
	private Vec2F acceleration;
	private Vec2F previousAcceleration;
	
	private float angle;
	private float angularVelocity;
	private float angularAcceleration;
	private float previousAngularAcceleration;
	
	private float mass;
	private float massVelocity;
	private float massDestination;
	private Vec2F size;
	
	//How far from center to edge when rendering
	private float xOffset,yOffset;
	
	
	public Metaball(Vec2F location, Vec2F size, float angle,float mass)
	{
		this(location,size, new Vec2F(0,0),angle,0,mass,0,mass);
	}

	public Metaball(Vec2F location, Vec2F size, Vec2F velocity, float angle, float angularVelocity, float mass, float massVelocity, float massDestination)
	{
		super();
		this.location = location;
		setSize(size);
		this.velocity = velocity;
		this.angle = angle;
		this.angularVelocity = angularVelocity;
		this.mass = mass;
		this.massVelocity = massVelocity;
		this.massDestination = massDestination;
		acceleration = new Vec2F(0.0f,0.0f);
		previousAcceleration = new Vec2F(0.0f,0.0f);;
		angularAcceleration = 0.0f;
		previousAngularAcceleration = 0.0f;
		massVelocity = 0.0f;
	}
	
	public void update(final float time)
	{
		Vec2F accelerationChange;
		float angularAccelerationChange;
		if(time > 0.0f)
		{
			accelerationChange = acceleration.sub(previousAcceleration).scale(1.0f / time);
			angularAccelerationChange = (angularAcceleration - previousAngularAcceleration) / time;
		}
		else
		{
			accelerationChange = new Vec2F(0,0);
			angularAccelerationChange = 0f;
		}
		
		location = location.add(velocity.scale(time));
		location = location.add(previousAcceleration.scale(0.5f * time * time));
		location = location.add(accelerationChange.scale(0.16666666666666f * time * time * time));
		velocity = velocity.add(previousAcceleration.scale(time));
		velocity = velocity.add(accelerationChange.scale(0.5f * time * time));
		previousAcceleration = acceleration;
		acceleration = new Vec2F(0.0f,0.0f);

		
		angle += angularVelocity * time;
		angle += 0.5f * previousAngularAcceleration * time * time;
		angle += 0.16666666666666f * angularAccelerationChange * time * time * time;
		
		angularVelocity += previousAngularAcceleration * time;
		angularVelocity += 0.5f * angularAccelerationChange * time * time;
		
		previousAngularAcceleration = angularAcceleration;
		angularAcceleration = 0.0f;
		
		if(massVelocity != 0.0f)
		{
        	mass += massVelocity;
        	if(massVelocity < 0.0f)
        	{
        		if(mass < massDestination)
        		{
        			mass = massDestination;
        			massVelocity = 0.0f;
        		}
        	}
        	else
        	{
        		if(mass > massDestination)
        		{
        			mass = massDestination;
        			massVelocity = 0.0f;
        		}
        	}
		}
	}
	public void accumulateAcceleration(final Vec2F acceleration)
	{
		this.acceleration = this.acceleration.add(acceleration);
	}
	public void accumulateAngularAcceleration(final float angularAcceleration)
	{
		this.angularAcceleration += angularAcceleration;
	}
	public final Vec2F getLocation()
	{
		return location;
	}
	public void setLocation(Vec2F location)
	{
		this.location = location;
	}
	public final float getAngle()
	{
		return angle;
	}
	public void setAngle(float angle)
	{
		this.angle = angle;
	}
	public final float getMass()
	{
		return mass;
	}
	public void setMass(float mass)
	{
		this.mass = mass;
	}

	final void appendRenderBuffers(final FloatBuffer vertexBuffer,final FloatBuffer massBuffer)
	{
		
		vertexBuffer.put(location.getX() - xOffset);
		vertexBuffer.put(location.getY() - yOffset);
		
		vertexBuffer.put(location.getX() + xOffset);
		vertexBuffer.put(location.getY() - yOffset);
		
		vertexBuffer.put(location.getX() + xOffset);
		vertexBuffer.put(location.getY() + yOffset);
		
		vertexBuffer.put(location.getX() - xOffset);
		vertexBuffer.put(location.getY() + yOffset);
		massBuffer.put(mass);
		massBuffer.put(mass);
		massBuffer.put(mass);
		massBuffer.put(mass);
	}
	
	public Vec2F getSize()
	{
		return size;
	}

	public void setSize(Vec2F size)
	{
		this.size = size;
		xOffset = size.getX()/2.0f;
		yOffset = size.getY()/2.0f;
	}

	public Vec2F getVelocity()
	{
		return velocity;
	}

	public void setVelocity(Vec2F velocity)
	{
		this.velocity = velocity;
	}

	public float getAngularVelocity()
	{
		return angularVelocity;
	}

	public void setAngularVelocity(float angularVelocity)
	{
		this.angularVelocity = angularVelocity;
	}

	public float getMassVelocity()
	{
		return massVelocity;
	}

	public void setMassVelocity(float massVelocity)
	{
		this.massVelocity = massVelocity;
	}

	public float getMassDestination()
	{
		return massDestination;
	}

	public void setMassDestination(float massDestination)
	{
		this.massDestination = massDestination;
	}
}
